import { Appender } from '../interface/Appender'
import { LoggerLevel, LoggerLevelDisplayName } from '../interface/LoggerLevel'

/**
 * BrowserConsoleRecorder
 *
 * @author 冰凝
 * @date 2022/12/10 20:48
 */
export class BrowserConsoleAppender implements Appender<BrowserConsoleRecorderConfig> {
    public config: BrowserConsoleRecorderConfig = {
        useTable: true,
        levelColor: {
            [LoggerLevel.TRACE]: '#717C86',
            [LoggerLevel.DEBUG]: '#40C2F8',
            [LoggerLevel.INFO]: '#128726',
            [LoggerLevel.WARN]: '#CABD2C',
            [LoggerLevel.ERROR]: '#FF0000',
        },
    }

    public append(name: string, level: LoggerLevel, ...args: Array<any>): void {
        if (this.config.useTable) {
            this.tablePrint(level, name, ...args)
            return
        }

        this.print(level, name, ...args)
    }

    private print(level: LoggerLevel, name: string, ...args: Array<any>) {
        switch (level) {
            case LoggerLevel.TRACE:
                console.debug(...this.formatInfo(LoggerLevel.TRACE, name), ...args)
                break
            case LoggerLevel.DEBUG:
                console.debug(...this.formatInfo(LoggerLevel.DEBUG, name), ...args)
                break
            case LoggerLevel.INFO:
                console.info(...this.formatInfo(LoggerLevel.INFO, name), ...args)
                break
            case LoggerLevel.WARN:
                console.warn(...this.formatInfo(LoggerLevel.WARN, name), ...args)
                break
            case LoggerLevel.ERROR:
                console.error(...this.formatInfo(LoggerLevel.ERROR, name), ...args)
                break
            default:
                throw new Error('未知日志等级')
        }
    }

    private tablePrint(level: LoggerLevel, name: string, ...args: Array<any>) {
        switch (level) {
            case LoggerLevel.TRACE:
                console.debug(...this.formatInfo(LoggerLevel.TRACE, name))
                console.table(...args)
                break
            case LoggerLevel.DEBUG:
                console.debug(...this.formatInfo(LoggerLevel.DEBUG, name))
                console.table(...args)
                break
            case LoggerLevel.INFO:
                console.info(...this.formatInfo(LoggerLevel.INFO, name))
                console.table(...args)
                break
            case LoggerLevel.WARN:
                console.warn(...this.formatInfo(LoggerLevel.WARN, name))
                console.table(...args)
                break
            case LoggerLevel.ERROR:
                console.error(...this.formatInfo(LoggerLevel.ERROR, name))
                console.table(...args)
                break
            default:
                throw new Error('未知日志等级')
        }
    }

    private formatInfo(level: LoggerLevel, name: string): Array<string> {
        return [
            `%c${ this.nowStr() } %c[${ LoggerLevelDisplayName[level] }]%c - ${ name }: `,
            'color: #717C86; background: #030307; padding: 5px 0;',
            `color: ${ this.config.levelColor[level] }; background: #030307; padding: 5px 0;`,
            'color: #ff66a5; background: #030307; padding: 5px 0;',
        ]
    }

    private nowStr(): string {
        const now = new Date()
        const date = [ now.getFullYear(), now.getMonth() + 1, now.getDate() ]
            .map(n => this.padded(n))
            .join('-')
        const time = [ now.getHours(), now.getMinutes(), now.getSeconds() ]
            .map(n => this.padded(n))
        time.push(this.padded(now.getMilliseconds(), 3))

        return date + ' ' + time.join(':')
    }

    private padded(num: number, length: number = 2): string | number {
        const s = String(Math.abs(num))
        if (s.length >= length) {
            return num
        }
        const padding = '0'.repeat(length - s.length)

        return num < 0
            ? '-' + padding + s
            : padding + s
    }
}

export interface BrowserConsoleRecorderConfig {
    /**
     * 为 true 时, 使用 {@link window.console.table} 并分两行输出, 具体效果依赖 {@link window.console.table} (已知多参数丢失table效果)
     * 为 false 时:
     * - {@link LoggerLevel.TRACE} -> {@link window.console.debug}
     * - {@link LoggerLevel.DEBUG} -> {@link window.console.debug}
     * - {@link LoggerLevel.INFO} -> {@link window.console.info}
     * - {@link LoggerLevel.WARN} -> {@link window.console.warn}
     * - {@link LoggerLevel.ERROR} -> {@link window.console.error}
     *
     * 默认 true
     */
    useTable: boolean

    /**
     * 不同日志等级颜色配置
     */
    levelColor: Record<LoggerLevel, string>
}
